package com.jellyfishumbrella.GL;

import java.util.ArrayList;

public class Polygon extends AbstractOctreeFiller {
	public boolean	isPolygonFan							= false;
	public MeshGL	mesh;
	public UberPoly	uberPoly;
	public Surface	surface;
	public int		surfacePolyGroupsRawVertOrderIndexStart	= Integer.MAX_VALUE;
	// normal dot P + d = 0
	public Normal	normal;
	public double	d;

	public Polygon(MeshGL mesh) {
		this.mesh = mesh;
		verts = new ArrayList<Vertex>();
		normal = new Normal();
	}
	
	public String toString(){
		String str = "";
		for(Vertex currVert : verts){
			if(currVert.getIndex() == -1){
				System.out.println("STOP");
			}
			str += currVert.toString() + "\n";
		}
		return str;
	}

	public Polygon(MeshGL mesh, ArrayList<Vertex> points) {
		this.mesh = mesh;
		verts = new ArrayList<Vertex>();
		for (Vertex currPoint : points) {
			verts.add(currPoint);
		}
		calcPolyNormal();
	}

	public boolean doesRayIntersect(Ray ray, double t0, double t1) {
		if (rayIntersection(ray, t0, t1) != null) {
			return true;
		} else {
			return false;
		}
	}

	public Vector3d rayIntersection(Ray ray, double t0, double t1) {
		double SMALL_NUM = 0.00000001;

		Vector3d intersection = null;
		if (isPolygonFan) {
			Vertex center = verts.get(0);
			for (int i = 1; i < verts.size() - 1; i++) { // cycle through the
															// triangles
				Vector3d u, v, n;
				Vector3d dir, w0, w;
				double r, a, b;

				u = new Vector3d();
				v = new Vector3d();

				u.sub(verts.get(i).coord, center.coord);
				v.sub(verts.get(i + 1).coord, center.coord);
				n = center.normal;

				dir = ray.direction.clone();// new Vector3d();
				w0 = new Vector3d();
				w = new Vector3d();

				// dir.sub(end, start);
				w0.sub(ray.origin, center.coord);
				a = -n.dot(w0);
				b = n.dot(dir);

				if (Math.abs(b) < SMALL_NUM) {
					intersection = null;
				} else {
					r = a / b;
					if (r < 0.0) {
						intersection = null;
					} else {

						intersection = new Vector3d();
						intersection.scale(r, dir);
						intersection.add(ray.origin);

						double uu, uv, vv, wu, wv, D;
						uu = u.dot(u);
						uv = u.dot(v);
						vv = v.dot(v);
						w.sub(intersection, center.coord);
						wu = w.dot(u);
						wv = w.dot(v);
						D = uv * uv - uu * vv;

						double s, t;
						s = (uv * wv - vv * wu) / D;
						if (s < 0.0 || s > 1.0) {
							intersection = null;
						} else {
							t = (uv * wu - uu * wv) / D;
							if (t < 0.0 || (s + t) > 1.0) {
								intersection = null;
							} else {
								double dist = ray.origin.distance(intersection);
								if (dist > t0 && dist < t1) {
									return intersection;
								} else {
									return null;
								}

							}
						}
					}
				}
			}
		}
		return intersection;
	}

	public void calcPolyNormal() {
		normal = new Normal();
		for (int i = 0; i < verts.size(); i++) {
			Normal n2 = new Normal();
			Vertex v;
			Vertex v2;
			if (i < verts.size() - 1) {
				v = verts.get(i);
				v2 = verts.get(i + 1);
			} else {
				v = verts.get(i);
				v2 = verts.get(0);
			}
			n2.x = (v.y() - v2.y()) * (v.z() + v2.z());
			n2.y = (v.z() - v2.z()) * (v.x() + v2.x());
			n2.z = (v.x() - v2.x()) * (v.y() + v2.y());
			// n2.normalize();
			normal.add(n2);
		}
		// n.scale(1.0 / (double) verts.size());
		normal.normalize();

		for (int i = 0; i < verts.size(); i++) {
			verts.get(i).normal = normal;
		}
	}

	public Polygon[] splitByPlane(Plane plane) {
		Polygon[] newPolys = new Polygon[3]; // newPolys[0] and newPolys[1] are
												// the original poly split in
												// two. newPolys[3] is the
												// portion of the plane
												// intersecting this Needs to be
												// stitched together with
												// results from other polys
		ArrayList<Vector3d> outPoints = new ArrayList<Vector3d>();
		ArrayList<Vector3d> inPoints = new ArrayList<Vector3d>();
		Vertex ptA, ptB;
		double sideA, sideB;
		Polygon planeAsPoly = new Polygon(mesh);

		int count = verts.size();
		if (count > 0) {
			ptA = verts.get(count - 1);
			sideA = plane.classifyPoint(ptA);

			for (int i = -1; ++i < count;) {
				ptB = verts.get(i);
				sideB = plane.classifyPoint(ptB);
				if (sideB > 0) {
					if (sideA < 0) {
						Vector3d v = new Vector3d();
						v.sub(ptB.coord, ptA.coord);
						double denom = plane.A * v.x + plane.B * v.y + plane.C * v.z;
						if (denom == 0) {

						} else {
							double sect = -plane.classifyPoint(ptA) / denom;
							Vector3d newPoint = new Vector3d();
							newPoint.scale(sect, v);
							newPoint.add(ptA.coord);
							outPoints.add(newPoint);
							inPoints.add(newPoint);
							planeAsPoly.verts.add(new Vertex(newPoint));
						}
					}
					outPoints.add(ptB.coord);
				} else if (sideB < 0) {
					if (sideA > 0) {
						Vector3d v = new Vector3d();
						v.sub(ptB.coord, ptA.coord);
						double denom = plane.A * v.x + plane.B * v.y + plane.C * v.z;
						if (denom == 0) {

						} else {
							double sect = -plane.classifyPoint(ptA) / denom;
							Vector3d newPoint = new Vector3d();
							newPoint.scale(sect, v);
							newPoint.add(ptA.coord);
							outPoints.add(newPoint);
							inPoints.add(newPoint);
							planeAsPoly.verts.add(new Vertex(newPoint));
						}
					}
					inPoints.add(ptB.coord);
				} else {
					outPoints.add(ptB.coord);
					inPoints.add(ptB.coord);
				}
				ptA = ptB;
				sideA = sideB;
			}

			Polygon front = new Polygon(mesh);
			for (Vector3d currVector : outPoints) {
				front.verts.add(new Vertex(currVector));
			}
			Polygon back = new Polygon(mesh);
			for (Vector3d currVector : inPoints) {
				back.verts.add(new Vertex(currVector));
			}
			newPolys[0] = front;
			newPolys[1] = back;
			newPolys[2] = planeAsPoly;
		}
		return newPolys;
	}
}
